﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  SubscribeTask.cs
//    
// Abstract:
//
//  This module implements the SubscribeTask class. This handles the task of subscribing for notifications on a folder via EWS.
//
//--

using System;
using System.Collections.Generic;
using System.Diagnostics;

using Microsoft.OfficeCommunicationsServer.Applications.Common;

namespace Microsoft.EnhancedReminders.Scheduler
{
    /// <summary>
    /// This handles the task of subscribing for notifications on a folder via EWS.
    /// </summary>
    internal class SubscribeTask : ExchangeTask
    {
        // Target folder for the subscription
        private readonly BaseFolderIdType _folderId;

        // Events to subscribe for
        private readonly NotificationEventTypeType[] _eventTypes;

        // Subscription ID of the latest subscription request
        private readonly Guid _subscriptionId;

        // Watermark associated with the last notification or status update
        private readonly string _watermark;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="taskContext">Task context</param>
        /// <param name="client">WCF proxy for communicating with EWS</param>
        /// <param name="impersonation">Exchange impersonation info</param>
        /// <param name="emailAddress">E-mail address of the user associated with this request</param>
        /// <param name="folderId">Target folder for subscription</param>
        /// <param name="eventTypes">Type of events to subscribe for</param>
        /// <param name="subscriptionId">ID to be associated with this subscription</param>
        /// <param name="watermark">Initial watermark for this subscription</param>
        internal SubscribeTask(TaskContext taskContext, ExchangeServicePortType client, ExchangeImpersonationType impersonation, string emailAddress, BaseFolderIdType folderId, NotificationEventTypeType[] eventTypes, Guid subscriptionId, string watermark)
            : base(taskContext, client, impersonation, emailAddress)
        {
            Debug.Assert(folderId != null, "folderId != null");
            Debug.Assert(eventTypes != null, "eventTypes != null");
            Debug.Assert(eventTypes.Length > 0, "eventTypes.Length > 0");
            Debug.Assert(subscriptionId != null, "subscriptionId != null");

            _folderId = folderId;
            _eventTypes = eventTypes;
            _subscriptionId = subscriptionId;
            _watermark = watermark;
        }

        /// <summary>
        /// Requests a subscription for the folder associated with this task.
        /// </summary>
        internal override void Begin()
        {
            Logger.WriteTraceVerbose(Context, "SubscribeTask.Begin");

            // Subscribe to events in the search folder
            PushSubscriptionRequestType pushSubscription = new PushSubscriptionRequestType();
            pushSubscription.FolderIds = new BaseFolderIdType[] { _folderId };
            pushSubscription.EventTypes = _eventTypes;
            pushSubscription.StatusFrequency = Monitor.SubscriptionStatusFrequency;

            // Add mailbox account and subscription ID to the query string
            pushSubscription.URL = ExchangeUsers.NotificationUrl + "?" + _emailAddress + ";" + _subscriptionId.ToString();
            // Set the watermark to the last known good watermark
            pushSubscription.Watermark = _watermark;

#if DEBUG
            // Verify that the subscription URL above is valid
            Uri resultUri;
            Debug.Assert(Uri.TryCreate(pushSubscription.URL, UriKind.Absolute, out resultUri), "Subscription URI is invalid");
#endif

            SubscribeType subscribe = new SubscribeType();
            subscribe.Item = pushSubscription;

            SubscribeRequest subscribeRequest = new SubscribeRequest();
            subscribeRequest.Subscribe = subscribe;
            subscribeRequest.ExchangeImpersonation = _impersonation;
            subscribeRequest.RequestServerVersion = ExchangeUsers.RequestServerVersion;

            Logger.WriteTraceInformation(Context, "Subscribing for notifications. using URL: {0}", pushSubscription.URL);

            try
            {
                // Send the subscribe request
                _client.BeginSubscribe(subscribeRequest, OnSubscribeCompleted, null);
            }
            catch (Exception ex)
            {
                HandleAccessError(ex, "BeginSubscribe failed.");
            }
        }

        /// <summary>
        /// Handles completion of the EWS request to subscribe for notifications.
        /// </summary>
        /// <param name="asyncResult">Status of the asynchronous operation</param>
        private void OnSubscribeCompleted(IAsyncResult asyncResult)
        {
            SubscribeResponse subscribeResponse = GetResponse<SubscribeResponse>(asyncResult, _client.EndSubscribe, "EndSubscribe failed.");

            if (subscribeResponse == null)
            {
                return;
            }

            ResponseMessageType responseMessage = subscribeResponse.SubscribeResponse1.ResponseMessages.Items[0];
            Debug.Assert(responseMessage != null, "responseMessage != null");

            Exception error = null;

            if (responseMessage.ResponseClass != ResponseClassType.Success)
            {
                // The subscription failed
                Logger.WriteTraceWarning(Context, "Subscription failed. Error: {0}", responseMessage.ResponseCode);
                error = new ExchangeTaskFailedException(Context, responseMessage.ResponseCode, "Subscription failed.");
            }
            else
            {
                // The subscription succeeded
                Logger.WriteTraceInformation(Context, "Subscription succeeded.");
            }

            RaiseCompleted(new TaskCompletedEventArgs(_taskContext, error));
        }
    }
}
